#include "base_socket.h"
#include <winsock.h>
#include <cstdio>
#include "guard.h"
#include "remote_obj_error.h"
using namespace RemoteObj;

Socket::~Socket()
{
}

int32_t Socket::open()
{
    return 0;
}

int32_t Socket::close()
{
    int32_t ret = 0;

    if (INVALID_SOCKET != _socket)
    {
        if(SOCKET_ERROR == ::closesocket(_socket))
        {
            return -1;
        }
        _socket = INVALID_SOCKET;
        ret = uninit();
    }
    return ret;
}

int32_t Socket::read(void* buffer, int32_t length)
{
    int32_t nread = 0;
    int32_t err = 0;

    if(!buffer)
    {
        return E_REMOTEOBJ_NULL_POINTER;
    }

    if(!is_open())
    {
        return -1;
    }

    while( nread < length)
    {
        int32_t xread = recv(_socket, (char*)buffer + nread, length - nread, 0);

        if(-1 == xread)
        {
            err = RemoteObjSockGetLastError();
            if(err == WSAEWOULDBLOCK)
            {
                continue;
            }
            return E_REMOTEOBJ_SYSERROR;
        }
        else if (0 == xread)
        {
            return -1;
        }
        nread += xread;
    }
    return 0;
}

int32_t Socket::write(const void* buffer, int32_t length)
{
    int32_t nwrite = 0;
    int32_t xwrite = 0;
    int32_t errno_cp = 0;
    if (!buffer)
    {
        return E_REMOTEOBJ_NULL_POINTER;
    }
    if (!is_open())
    {
        return -1;
    }

    while(nwrite < length)
    {
        xwrite = send(_socket, (const char*)buffer + nwrite, length - nwrite, 0);
        if (-1 == xwrite)
        {
            errno_cp = RemoteObjGetLastError();
            if(errno_cp == WSAEWOULDBLOCK)
            {
                xwrite = 0;
            }
            else
            {
                return -1;
            }
        }

        nwrite += xwrite;
    }
    return 0;
}

bool Socket::is_open() const
{
    return true;
}

bool Socket::peek() const
{
    char buffer[1];
    int32_t ret  = 0;

    if (!is_open())
    {
        return false;
    }

    ret = recv(_socket, buffer, 1, MSG_PEEK);
    if (ret == -1)
    {
        return false;
    }
    return (ret == 1);
}

Socket::Socket()
    :_socket(INVALID_SOCKET),
    _send_timeout(5),
    _recv_timeout(5)
{
    init();
}

int32_t Socket::init()
{
    Guard<Mutex> guard(&_mutex);
    int32_t errno_cp = 0;
    if (0 == _nofSockets)
    {
        WSADATA info;
        int32_t ret = 0;
        ret = WSAStartup(MAKEWORD(2, 0), &info);
        if (0 != ret)
        {
            errno_cp = RemoteObjGetLastError();
            return errno_cp;
        }
    }
    ++_nofSockets;

    return 0;
}

int32_t Socket::uninit()
{
    Guard<Mutex> guard(&_mutex);
    int32_t errno_cp = 0;
    if (0 == --_nofSockets)
    {
        if (0 != WSACleanup())
        {
            errno_cp = RemoteObjGetLastError();
            return errno_cp;
        }
    }

    return 0;
}

int32_t Socket::set_timeout(int32_t seconds, int32_t optname)
{
    int32_t ret = 0;

    if (!is_open())
    {
        return 0;
    }

    if (seconds < 0)
    {
        seconds = 0;
    }

    if (SO_SNDTIMEO == optname)
    {
        _send_timeout = seconds;
    }
    else
    {
        _recv_timeout = seconds;
    }

    int32_t tv = seconds;
    ret = setsockopt(_socket, SOL_SOCKET, optname, (const char*)&tv, sizeof(int32_t));

    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

int32_t Socket::set_send_timeout(int32_t seconds)
{
    return set_timeout(seconds,SO_SNDTIMEO);
}

int32_t Socket::set_recv_timeout(int32_t seconds)
{
    return set_timeout(seconds,SO_RCVTIMEO);
}

int32_t Socket::_nofSockets = 0;
Mutex Socket::_mutex;

